package com.bungarus.rm.repo;

import com.bungarus.rm.model.Containable;
import com.bungarus.rm.model.IDMakerScope;
import com.bungarus.rm.model.UniqueNodeID;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.sql.PreparedStatement;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by tang on 2019/1/14.
 */
@Component
public class MsgIDScopeRepoImpl implements MsgIDScopeRepository {
    Logger logger = Logger.getLogger(MsgIDScopeRepoImpl.class);

    private Map<UniqueNodeID, Containable> cache;

    private JdbcTemplate jdbcTemplate;

    @Autowired
    public MsgIDScopeRepoImpl(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @PostConstruct
    public void init() {
        cache = new ConcurrentHashMap<>();
    }

    @PreDestroy
    public void destroy() {
        if(!cache.isEmpty()) {
            cache.clear();
        }
    }

    @Override
    public Optional<Containable> findInCache(UniqueNodeID uniqueId) {
        if(null == uniqueId) {
            throw new IllegalArgumentException("id required.");
        }
        return Optional.ofNullable(cache.get(uniqueId));
    }

    @Override
    public boolean saveInCache(UniqueNodeID uniqueNodeID, Containable scope) {
        if(null == scope) {
            return false;
        }
        assert scope instanceof IDMakerScope;
        Containable oldValue = cache.putIfAbsent(uniqueNodeID, scope);
        return null == oldValue;
    }

    @Override
    public Optional<? extends Containable> invalidScopeInCache(UniqueNodeID id) {
        return Optional.ofNullable(cache.remove(id));
    }

    @Override
    public Optional<IDMakerScope> findById(UniqueNodeID uniqueNodeID) {
        String querySql = "select node_id, cluster_id, id_begin, id_end from t_id_scope where node_id=? and cluster_id=?";
        IDMakerScope result = null;
        try {
            result = this.jdbcTemplate.queryForObject(querySql,
                    new Object[]{uniqueNodeID.getNodeId(), uniqueNodeID.getClusterId()},
                    (resultSet, i) -> {
                        IDMakerScope scope = IDMakerScope.newBuilder()
                                .cluster(resultSet.getInt("cluster_id"))
                                .node(resultSet.getInt("node_id"))
                                .worker(resultSet.getInt("id_end"))
                                .dc(resultSet.getInt("id_begin"))
                                .build();
                        return scope;
                    });
        }
        catch (DataAccessException e) {
            if(!(e instanceof EmptyResultDataAccessException)) {
                e.printStackTrace();
            }
        }
        return Optional.ofNullable(result);
    }

    @Override
    public List<IDMakerScope> findAllScopes() {
        return null;
    }

    @Override
    public void saveScope(IDMakerScope scope) {
        if(null != scope) {
            Optional<IDMakerScope> temp = findById(new UniqueNodeID(scope.getId().getClusterId(),scope.getId().getNodeId()));
            if(!temp.isPresent()) {
                String insertSql = "insert into t_id_scope (node_id, cluster_id, id_begin, id_end) values (?, ?, ?, ?)";
                this.jdbcTemplate.execute(insertSql, (PreparedStatement preparedStatement) -> {
                    preparedStatement.setShort(1, (short) scope.getId().getNodeId());
                    preparedStatement.setByte(2, (byte) scope.getId().getClusterId());
                    preparedStatement.setByte(3, (byte) scope.getDc());
                    preparedStatement.setByte(4, (byte) scope.getWorker());
                    return preparedStatement.execute();
                });
            }
            else {
                String updateSql = "update t_id_scope set id_begin=?, id_end=? where node_id=? and cluster_id=?";
                this.jdbcTemplate.update(updateSql, setter -> {
                    setter.setByte(1, (byte)scope.getDc());
                    setter.setByte(2, (byte)scope.getWorker());
                    setter.setShort(3, (short)scope.getId().getNodeId());
                    setter.setByte(4, (byte)scope.getId().getClusterId());
                });
            }
        }
    }

    @Override
    public List<String> findAllScopePlaceholders() {
        String querySql = "select dc_wk from t_scope_placeholder";
        return this.jdbcTemplate.queryForList(querySql, String.class);
    }

    @Override
    public void saveScopePlaceholder(int dcholder, int wkholder) {
        String insertSql = "insert into t_scope_placeholder (dc_wk) values (?)";
        this.jdbcTemplate.execute(insertSql, (PreparedStatement preparedStatement) -> {
            preparedStatement.setString(1, dcholder + MsgIDScopeRepository.SCOPE_DELIMIT + wkholder);
            return preparedStatement.execute();
        });
    }

    @Override
    public void updateScopePlaceholder(int dcholder, int wkholder, int oldwk) {
        String updateSql = "update t_scope_placeholder set dc_wk = ? where dc_wk = ?";
        this.jdbcTemplate.update(updateSql, preparedStatement -> {
            preparedStatement.setString(1, dcholder + MsgIDScopeRepository.SCOPE_DELIMIT + wkholder);
            preparedStatement.setString(2, dcholder + MsgIDScopeRepository.SCOPE_DELIMIT + oldwk);
        });
    }

    @Override
    public void deleteById(UniqueNodeID id) {
        String deleteSql ="delete from t_id_scope where node_id=? and cluster_id=? ";
        jdbcTemplate.execute(deleteSql, (PreparedStatement preparedStatement) -> {
            preparedStatement.setShort(1, (short)id.getNodeId());
            preparedStatement.setByte(2, (byte)id.getClusterId());
            return preparedStatement.execute();
        });
    }
}
